
#ifndef EDGE_CELL_H
#define EDGE_CELL_H

#include <BMap>
#include <BMesh>
#include <BGeometry>

using namespace BWE;

struct Edge
{
	inline Edge()
	{
		this->a = 0;
		this->b = 0;
	}
	inline Edge(int a, int b)
	{
		if (a > b)
		{
			this->a = b;
			this->b = a;
		}
		else
		{
			this->a = a;
			this->b = b;
		}
	}
	inline Edge(int a, int b, const BVector& va, const BVector& vb)
	{
		if (a > b)
		{
			this->a = b;
			this->b = a;
			this->va = vb;
			this->vb = va;
		}
		else
		{
			this->a = a;
			this->b = b;
			this->va = va;
			this->vb = vb;
		}
	}
	inline void swap()
	{
		bSwap(a, b);
		bSwap(va, vb);
	}
	inline void correct()
	{
		if (a > b)
		{
			bSwap(a, b);
			bSwap(va, vb);
		}
	}
	inline bool equal(int ia, int ib)
	{
		if (a == ia && b == ib)
			return true;
		if (a == ib && b == ia)
		{
			bSwap(a, b);
			bSwap(va, vb);
			return true;
		}
		return false;
	}
	inline bool operator == (const Edge& edge) const
	{
		if (a == edge.a && b == edge.b)
			return true;
		if (a == edge.b && b == edge.a)
			return true;
		return false;
	}
	inline bool operator != (const Edge& edge) const
	{
		if (a == edge.a && b == edge.b)
			return false;
		if (a == edge.b && b == edge.a)
			return false;
		return true;
	}
	inline bool operator < (const Edge& edge) const
	{
		if (a == edge.a)
			return b < edge.b;
		return a < edge.a;
	}
	inline bool operator > (const Edge& edge) const
	{
		if (a == edge.a)
			return b > edge.b;
		return a > edge.a;
	}
	int a;
	int b;
	BVector	va;
	BVector	vb;
};
typedef BArray<Edge>	EdgeArray;
typedef BSet<Edge>		EdgeSet;

struct Cell : public BThing
{
	BVector normal;
	virtual BVector center() const = 0;
	virtual bool contain(const Edge& edge) const = 0;
};
struct TrigCell : public Cell
{
	inline TrigCell(int a, int b, int c, const BVector& va, const BVector& vb, const BVector& vc) :
		edge_ab(a, b, va, vb),
		edge_bc(b, c, vb, vc),
		edge_ca(c, a, vc, va)
	{
		this->ia = a;
		this->ib = b;
		this->ic = c;
		this->va = va;
		this->vb = vb;
		this->vc = vc;
		normal = (vb - va).cross(vc - va);
		normal.normalize();
	}
	inline bool equal(int a, int b, int c) const
	{
		return ia == a && ib == b && ic == c;
	}
	virtual BVector center() const
	{
		return (va + vb + vc) * 0.333333333;
	}
	virtual bool contain(const Edge& edge) const
	{
		return edge_ab == edge || edge_bc == edge || edge_ca == edge;
	}
	int ia;
	int ib;
	int ic;
	BVector	va;
	BVector	vb;
	BVector	vc;
	Edge	edge_ab;
	Edge	edge_bc;
	Edge	edge_ca;
};
struct QuadCell : public Cell
{
	QuadCell(int a, int b, int c, int d, const BVector& va, const BVector& vb, const BVector& vc, const BVector& vd) :
		edge_ab(a, b, va, vb),
		edge_bc(b, c, vb, vc),
		edge_cd(c, d, vc, vd),
		edge_da(d, a, vd, va)
	{
		this->ia = a;
		this->ib = b;
		this->ic = c;
		this->id = d;
		this->va = va;
		this->vb = vb;
		this->vc = vc;
		this->vd = vd;
		normal = (vb - va).cross(vc - va);
		normal.normalize();
	}
	inline bool equal(int a, int b, int c, int d) const
	{
		return ia == a && ib == b && ic == c && id == d;
	}
	virtual BVector center() const
	{
		return (va + vb + vc + vd) * 0.25;
	}
	virtual bool contain(const Edge& edge) const
	{
		return edge_ab == edge || edge_bc == edge || edge_cd == edge || edge_da == edge;
	}
	int ia;
	int ib;
	int ic;
	int id;
	BVector	va;
	BVector	vb;
	BVector	vc;
	BVector	vd;
	Edge	edge_ab;
	Edge	edge_bc;
	Edge	edge_cd;
	Edge	edge_da;
};
struct PolyCell : public Cell
{
	BVectorArray	vertices;
};
typedef BArray<Cell*>		CellArray;
typedef BSet<Cell*>			CellSet;
typedef BHolder<Cell>		CellHolder;
typedef BArray<CellHolder>	CellHolderArray;
typedef BSet<CellHolder>	CellHolderSet;

struct Neighbor
{
	inline void addCell(Cell* cell)
	{
		if (cellA)
			cellB = cell;
		else
			cellA = cell;
	}
	CellHolder		cellA;
	CellHolder		cellB;
};

typedef BMap<Edge, Neighbor>		EdgeNeighborMap;

inline const Edge& FlatEdge(int a, int b, const BVector& va, const BVector& vb)
{
	static Edge edge;
	edge.a = a;
	edge.b = b;
	edge.va = va;
	edge.vb = vb;
	return edge;
}

bool CreateEdgeNeighborMap(const BGeometry* geometry, BElement* element, EdgeNeighborMap& edgeNeighborMap);

#endif
